/* CLOPTS.C
 * Copyright (c) 2007 MissingNODE 
 * Based on GPL OpenQM Code
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * 
 * 
 * START-HISTORY:
 * Oct-10-2007 Initial Revision
 * END-HISTORY
 *
 * START-DESCRIPTION:
 *
 * -K         Kill user(s)
 * -RECOVER   Recover users
 * -U         Show users
 *
 * END-DESCRIPTION
 *
 * START-CODE
 */
#include <signal.h>
#include <tm.h>
#include <locks.h>

private bool process_exists(int pid);
void remove_user(USER_ENTRY * uptr);

/* ======================================================================
   recover_users()  -  Recover licence space for vanished users           */

bool recover_users()
{
 bool status = FALSE;
 USER_ENTRY * uptr;
 long int pid;
 short int u;
 short int user_no;

 /* Be brutal - Lock everything in sight */

 StartExclusive(FILE_TABLE_LOCK,45);
 StartExclusive(REC_LOCK_SEM,45);
 StartExclusive(GROUP_LOCK_SEM,45);
 StartExclusive(SHORT_CODE,45);

 for (u = 1; u <= sysseg->max_users; u++)
  {
   uptr = UPtr(u);
   user_no = uptr->uid;
   pid = uptr->pid;
   if (uptr->uid)
    {
     if (!process_exists(pid))
      {
       remove_user(uptr);
       tio_printf("Removed user %d (pid %ld)\n", (int)user_no, pid);
       status = TRUE;
      }
    }
  }


 EndExclusive(SHORT_CODE);
 EndExclusive(GROUP_LOCK_SEM);
 EndExclusive(REC_LOCK_SEM);
 EndExclusive(FILE_TABLE_LOCK);

 return status;
}

/* ======================================================================
   show_users()  -  Display user information (tm -U)                      */

void show_users()
{
 int i;
 USER_ENTRY * uptr;

 if (!attach_shared_memory())
  {
   fprintf(stderr, "TextMark is not active\n");
   return;
  }

 /* Users
    0         1         2         3         4         5         6         7
    01234567890123456789012345678901234567890123456789012345678901234567890123456789
     Uid Pid........ Puid Origin......... Username........................
    1234 12345678901 1234 123.123.123.123 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/

 printf(" Uid Pid........ Puid Origin......... Username\n");
 for(i = 1; i <= sysseg->max_users; i++)
  {
   uptr = UPtr(i);
   if (uptr->uid != 0)
    {
     printf("%4hd %11ld %4d %-15s %-32s\n",
            uptr->uid, uptr->pid, (int)(uptr->puid),
            (uptr->ttyname[0] != '\0')?uptr->ttyname:uptr->ip_addr,
            uptr->username);
    }
  }

 unbind_sysseg();
}

/* ======================================================================
   kill_user()  -  Kill user process command line option                  */

void kill_user(uid)
   int uid;         /* User number. Zero to kill all */
{
 USER_ENTRY * uptr;
 short int u;
 short int user_no;
 int pid;
 char errmsg[80+1];

 if (!attach_shared_memory())
  {
   fprintf(stderr, "TextMark is not active\n");
   return;
  }

 if (!get_semaphores(FALSE, errmsg))
  {
   fprintf(stderr, "Cannot access semaphores\n");
   return;
  }

 StartExclusive(FILE_TABLE_LOCK,68);
 StartExclusive(REC_LOCK_SEM,68);
 StartExclusive(GROUP_LOCK_SEM,68);
 StartExclusive(SHORT_CODE,68);


 if (uid == 0)          /* Kill all users */
  {
   for (u = 1; u <= sysseg->max_users; u++)
    {
     uptr = UPtr(u);
     if (uptr->uid != 0)
      {
       uptr->events |= (uptr->flags & USR_LOGOUT)?EVT_LOGOUT:EVT_TERMINATE;
       uptr->flags |= USR_LOGOUT;
      }
    }
  }
 else                   /* Kill specific user */
  {
   if ((uid < 1) || (uid > sysseg->max_users))
    {
     fprintf(stderr, "Invalid user number\n");
    }
   else
    {
     uptr = UserPtr(uid);
     if (uptr != NULL)
      {
       user_no = uptr->uid;
       pid = uptr->pid;

       /* Check that the process actually exists */

       if (process_exists(pid))
        {
         uptr->events |= (uptr->flags & USR_LOGOUT)?EVT_LOGOUT:EVT_TERMINATE;
         uptr->flags |= USR_LOGOUT;
        }
       else
        {
         remove_user(uptr);
         printf("Removed user %d (pid %d)\n", (int)user_no, pid);
        }
      }
     else
      {
       fprintf(stderr, "User is not active\n");
      }
    }
  }

 EndExclusive(SHORT_CODE);
 EndExclusive(GROUP_LOCK_SEM);
 EndExclusive(REC_LOCK_SEM);
 EndExclusive(FILE_TABLE_LOCK);

 unbind_sysseg();
}

/* ======================================================================
   cleanup()  -  Clean up user tables from lost processes                 */

void cleanup()
{
 USER_ENTRY * uptr;
 short int u;
 short int user_no;
 int pid;
 char username[MAX_USERNAME_LEN+1];  /* Login user name */
 char errmsg[80+1];

 if (!attach_shared_memory())
  {
   fprintf(stderr, "TextMark is not active\n");
   return;
  }

 if (!get_semaphores(FALSE, errmsg))
  {
   fprintf(stderr, "Cannot access semaphores\n");
   return;
  }

 StartExclusive(FILE_TABLE_LOCK,59);
 StartExclusive(REC_LOCK_SEM,59);
 StartExclusive(GROUP_LOCK_SEM,59);
 StartExclusive(SHORT_CODE,59);

 for (u = 1; u <= sysseg->max_users; u++)
  {
   uptr = UPtr(u);
   if (uptr->uid != 0)
    {
     pid = uptr->pid;
     if (!process_exists(pid))
      {
       user_no = uptr->uid;
       strcpy(username, (char *)(uptr->username));
       remove_user(uptr);
       log_printf("Cleanup removed user %d (pid %d, %s)\n", (int)user_no, pid, username);
      }
    }
  }


 EndExclusive(SHORT_CODE);
 EndExclusive(GROUP_LOCK_SEM);
 EndExclusive(REC_LOCK_SEM);
 EndExclusive(FILE_TABLE_LOCK);

 unbind_sysseg();
}

/* ======================================================================
   suspend_resume()                                                       */

void suspend_resume(bool suspend)
{
 if (!attach_shared_memory())
  {
   fprintf(stderr, "TextMark is not active\n");
   return;
  }

 if (suspend) sysseg->flags |= SSF_SUSPEND;
 else sysseg->flags &= ~SSF_SUSPEND;

 unbind_sysseg();
}

/* ====================================================================== */

private bool process_exists(int pid)
{

 return (!kill(pid, 0) || (errno == EPERM));
}

/* ====================================================================== */

void remove_user(USER_ENTRY * uptr)
{
 short int i;
 short int user_no;
 long int pid;
 FILE_ENTRY * fptr;
 RLOCK_ENTRY * lptr;
 GLOCK_ENTRY * gptr;
 unsigned short int * ufm;


 user_no = uptr->uid;
 pid = uptr->pid;

 /* Give away process locks */

 for (i = 0; i < 64; i++)
  {
   if (sysseg->task_locks[i] == process.user_no) sysseg->task_locks[i] = 0;
  }

 /* Give away file locks */

 for (i = 1; i <= sysseg->used_files; i++)
  { 
   fptr = FPtr(i);
   if (fptr->ref_ct != 0)  /* File entry is in use */
    {
     if (abs(fptr->file_lock) == user_no)
      {
       fptr->file_lock = 0;
       clear_waiters(-i);
       (fptr->ref_ct)--;   /* Must have been open to us */
      }
    }
  }

 /* Give away record locks */

 for (i = 1; i <= sysseg->num_rlocks; i++)
  {
   lptr = RLPtr(i);

   if ((lptr->hash != 0) && (lptr->owner == user_no))
    {
     /* We have found a lock to release */
     (RLPtr(lptr->hash)->count)--;
     (FPtr(lptr->file_id)->lock_count)--;
     lptr->hash = 0;    /* Free this cell */
     if (lptr->waiters) clear_waiters(i);
    }
  }

 /* Give away group locks */

 for (i = 1; i <= sysseg->num_glocks; i++)
  {
   gptr = GLPtr(i);

   if ((gptr->hash != 0) && (gptr->owner == user_no))
    {
     /* We have found a lock to release */
     (GLPtr(gptr->hash)->count)--;
     gptr->hash = 0;    /* Free this cell */
    }
  }

 /* Give away file table entries */

 for (i = 1; i <= sysseg->num_files; i++)
  {
   ufm = UFMPtr(uptr,i);
   if (*ufm)
    {
     /* The following must allow for a reference count of -1 which
        indicates exclusive access to the file.                    */

     fptr = FPtr(i);
     fptr->ref_ct = abs(fptr->ref_ct) - *ufm;
    }
  }


 /* Release user table entry */

 *(UMap(uptr->uid)) = 0;
 uptr->uid = 0;
 uptr->pid = 0;
}

